package com.mini.grpc.transport.netty;

import com.mini.grpc.transport.ServerListener;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.HttpServerUpgradeHandler;
import io.netty.handler.codec.http2.DefaultHttp2Connection;
import io.netty.handler.codec.http2.Http2CodecUtil;
import io.netty.handler.codec.http2.Http2Connection;
import io.netty.handler.codec.http2.Http2ConnectionHandler;
import io.netty.handler.codec.http2.Http2ConnectionHandlerBuilder;
import io.netty.handler.codec.http2.Http2FrameLogger;
import io.netty.handler.codec.http2.Http2ServerUpgradeCodec;
import io.netty.handler.codec.http2.HttpToHttp2ConnectionHandlerBuilder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.ssl.ApplicationProtocolNames;
import io.netty.handler.ssl.ApplicationProtocolNegotiationHandler;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Http2ServerInitializer用于初始化HTTP/2服务端处理器。
 */
public class Http2ServerInitializer extends ChannelInitializer<SocketChannel> {
    
    private static final Logger logger = LoggerFactory.getLogger(Http2ServerInitializer.class);
    
    private final ServerListener listener;
    private static final Http2FrameLogger frameLogger = new Http2FrameLogger(LogLevel.DEBUG, Http2ServerInitializer.class);
    
    /**
     * 创建一个新的Http2ServerInitializer
     *
     * @param listener 服务端监听器
     */
    public Http2ServerInitializer(ServerListener listener) {
        this.listener = listener;
    }
    
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        final ChannelPipeline p = ch.pipeline();
        
        // 检查是否已经通过ALPN协商使用HTTP/2
        if (ch.pipeline().get(ApplicationProtocolNegotiationHandler.class) != null) {
            // SSL连接，已经通过ALPN协商使用HTTP/2
            configureHttp2(p);
        } else {
            // 非SSL连接，直接配置HTTP/2处理器
            // 因为我们的客户端直接发送HTTP/2连接前言
            configureHttp2(p);
        }
    }
    
    /**
     * 配置HTTP/2处理器
     *
     * @param pipeline 通道管道
     */
    private void configureHttp2(ChannelPipeline pipeline) {
        DefaultHttp2Connection connection = new DefaultHttp2Connection(true);
        NettyServerHandler serverHandler = new NettyServerHandler(connection, listener);
        
        // 创建HTTP/2连接处理器，并设置我们的帧监听器
        Http2ConnectionHandler connectionHandler = new Http2ConnectionHandlerBuilder()
            .frameListener(serverHandler)
            .connection(connection)
            .frameLogger(frameLogger)
            .build();
        
        pipeline.addLast(connectionHandler);
        pipeline.addLast(new Http2HandlerAdapter(serverHandler, connectionHandler));
        pipeline.addLast(new ServerExceptionHandler());
    }
    
    /**
     * 配置HTTP升级到HTTP/2
     *
     * @param pipeline 通道管道
     */
    private void configureHttpUpgrade(ChannelPipeline pipeline) {
        final HttpServerCodec sourceCodec = new HttpServerCodec();
        
        pipeline.addLast(sourceCodec);
        pipeline.addLast(new HttpServerUpgradeHandler(sourceCodec, protocol -> {
            if (ApplicationProtocolNames.HTTP_2.equals(protocol)) {
                DefaultHttp2Connection connection = new DefaultHttp2Connection(true);
                NettyServerHandler serverHandler = new NettyServerHandler(connection, listener);
                
                Http2ConnectionHandlerBuilder builder = new Http2ConnectionHandlerBuilder()
                    .frameListener(serverHandler)
                    .connection(connection)
                    .frameLogger(frameLogger);
                
                Http2ConnectionHandler connectionHandler = builder.build();
                
                // 创建适配器来设置上下文
                return new Http2ServerUpgradeCodec(connectionHandler) {
                    @Override
                    public void upgradeTo(ChannelHandlerContext ctx, FullHttpRequest upgradeRequest) {
                        try {
                            super.upgradeTo(ctx, upgradeRequest);
                            // 设置上下文和编码器到服务端处理器
                            serverHandler.setContext(ctx, connectionHandler.encoder());
                        } catch (Exception e) {
                            logger.error("Failed to upgrade to HTTP/2", e);
                            ctx.close();
                        }
                    }
                };
            } else {
                return null;
            }
        }));
        
        pipeline.addLast(new UpgradeRequestHandler());
        pipeline.addLast(new ServerExceptionHandler());
    }
    
    /**
     * HTTP/2处理器适配器，用于设置上下文
     */
    private static class Http2HandlerAdapter extends ChannelInboundHandlerAdapter {
        private final NettyServerHandler serverHandler;
        private final Http2ConnectionHandler connectionHandler;
        
        public Http2HandlerAdapter(NettyServerHandler serverHandler, Http2ConnectionHandler connectionHandler) {
            this.serverHandler = serverHandler;
            this.connectionHandler = connectionHandler;
        }
        
        @Override
        public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
            super.handlerAdded(ctx);
            // 设置上下文和编码器到服务端处理器
            serverHandler.setContext(ctx, connectionHandler.encoder());
        }
    }
    
    /**
     * 处理不支持HTTP/2升级的请求
     */
    private static class UpgradeRequestHandler extends ChannelInboundHandlerAdapter {
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            // 如果到达这里，说明不支持HTTP/2升级，关闭连接
            logger.warn("HTTP/2 upgrade not supported, closing connection");
            ctx.close();
        }
    }
    
    /**
     * 处理服务端异常
     */
    private static class ServerExceptionHandler extends ChannelInboundHandlerAdapter {
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            logger.error("Exception in HTTP/2 server", cause);
            ctx.close();
        }
    }
} 